﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Reflection;

namespace ExecutionContract
{
    public class ExecutionContractReader : IExecutionContractReader
    {
        #region Properties

        private HashSet<Type> TypeSet { get; set; }

        /// <summary>
        /// This Writer generates the xml code that contains the logic of the element
        /// </summary>
        public IExecutionContractWriter Writer { get; set; }

        #endregion

        #region Constructors

        public ExecutionContractReader()
        {
            TypeSet = new HashSet<Type>();

        }

        public ExecutionContractReader(IExecutionContractWriter _writer)
        {
            TypeSet = new HashSet<Type>();
            Writer = _writer;
        }
        #endregion

        #region Public Methods

        /// <summary>
        /// Adds a type to the reader. I the same type was added before the reader will return false and the type will not be added to the reader. This is to avoid unnecessary operations of the reader. 
        /// </summary>
        /// <param name="TypeToCheck">Type that should be added to the reader.</param>
        /// <returns>bool parameter that describes if the type was added or not.</returns>
        public bool Add(Type TypeToAdd)
        {
            if (AddNecessary(TypeToAdd))
            {
                bool AddParameter = TypeSet.Add(TypeToAdd);

                //Check if add was successful in otherways throw an exception to inform the user about the problem
                if (!AddParameter)
                {
                    throw new Exception("The type could not be added to the reader.");
                }
                return true;
            }
            else
            {
                // the type is already contained in the TypeSet
                return false;
            }
        }
        /// <summary>
        /// This method checks if it is necessary to add the type to the reader
        /// </summary>
        /// <param name="TypeToAdd">type that shoudl be add</param>
        /// <returns></returns>
        public bool AddNecessary(Type TypeToAdd){
            return !this.TypeSet.Contains(TypeToAdd);
        }
        /// <summary>
        /// This methods goes through all assigend types and checks whether the types contain any ExecutionContractMemberAttribute.
        /// </summary>
        /// <returns>List of all members that contain a ExecutionContractMemberAttribute</returns>
        public List<ExecutionContract> Read()
        {
            List<ExecutionContract> OutValue = new List<ExecutionContract>();

            List<MemberInfo> MemberInfoForType = null;
            foreach (Type Foo in TypeSet)
            {
                MemberInfo[] MembersInfo = Foo.GetMembers();
                MemberInfoForType = new List<System.Reflection.MemberInfo>();

                //Go through all members
                foreach (MemberInfo MemberInfo in MembersInfo)
                {
                    //check for the attribute
                    if (ContainsAttribute(MemberInfo))
                    {
                        //add the memberinfo to the output
                        MemberInfoForType.Add(MemberInfo);
                    }
                }

                //if there are any members in the MemberInforForType then add to output
                if (MemberInfoForType.Any())
                {
                    OutValue.Add(new ExecutionContract(Foo, MemberInfoForType));
                }
            }

            return OutValue;
        }
        #endregion

        #region Private Methods

        /// <summary>
        /// This method checks whether a member contains an ExecutionContractMemberAttribute.
        /// </summary>
        /// <param name="MemberInfo">Member that should be checked</param>
        /// <returns>bool value that indicates whether a member contains the attribute or not.</returns>
        private static bool ContainsAttribute(MemberInfo MemberInfo)
        {
            Type ContractMemberType = typeof(ExecutionMemberAttribute);
            LinkedList<ExecutionMemberAttribute> retList = new LinkedList<ExecutionMemberAttribute>();

            foreach (Attribute CustomAttribute in MemberInfo.GetCustomAttributes(true))
            {
                if (ContractMemberType.Equals(CustomAttribute.GetType()))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// This method checks whether a member contains an ExecutionContractMemberAttribute.
        /// </summary>
        /// <param name="MemberInfo">Member that should be checked</param>
        /// <param name="MemberType">out parameter, that indicates the type of the member</param>
        /// <returns>bool value that indicates whether a member contains the attribute or not.</returns>
        private bool ContainsAttribute(MemberInfo MemberInfo, out MemberTypes MemberType)
        {
            //overload to get the membertype directly
            MemberType = MemberInfo.MemberType;

            return ContainsAttribute(MemberInfo);
        }
        #endregion



    }
}